package com.simple.datax.parse.impl;

import java.util.ArrayList;
import java.util.Iterator;

import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlValidationError;

import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.rpc.RpcException;
import com.simple.datax.SimpleMessage;
import com.simple.datax.api.CommonSimpleException;
import com.simple.datax.api.NeedResendSimpleException;
import com.simple.datax.api.Parser;
import com.simple.datax.api.ParserException;
import com.simple.datax.api.SimpleException;
import com.simple.datax.service.AbstractMessageProcessor;
import com.simple.datax.utils.SimpleConstants;
import com.simple.datax.xml.ec.EC101MessageDocument;
import com.simple.datax.xml.ec.EC101ResponseDocument;
import com.simple.datax.xml.tcs.TCS101MessageDocument;
import com.simple.datax.xml.tcs.TCS101ResponseDocument;

/**
 * @Title: ParserImpl.java
 * @Package com.simple.datax.parse.impl
 * @Description: 
 *               报文解析器，用来生产报文XML类型；对于是输入是XML的报文，直接使用xmlbeans解析，并使用XSD校验；对于非XML的报文
 *               ，直接解析成EC内部报文格式。（相当于完成了解析和向内部报文转换的两步动作）
 * @author Paladin Chen
 * @date 2012-12-28 下午2:53:08
 * @version V1.0
 */
public class ParserImpl extends AbstractMessageProcessor implements Parser {
	private static final Logger logger = LoggerFactory.getLogger(ParserImpl.class);

	// 最大重发次数
	private int maxCount = 3;

	@Override
	public void realProcess(SimpleMessage src) throws SimpleException {
		logger.debug("Begin to Parser......" + src.toLog());

		try {
			setErrorInfos(SimpleConstants.ERROR_TYPE_DATAXPROC, SimpleConstants.ERROR_LEVEL_NORMAL,
					SimpleConstants.RESPONSE_CODE_PARSE, SimpleConstants.RESPONSE_MSG_PARSE);

			parse((SimpleMessage) src);

			procResendMax(src);

			// 需要备份解析后的报文，对于重发报文，如果是已经解析备份过的，则不需再备份
			// 同样应答也只处理非重发消息
			if (src.getResendState() < SimpleConstants.STATUS_PARSE) {
				this.callResponseService(src, SimpleConstants.RESPONSE_CODE_NORMAL, SimpleConstants.RESPONSE_MSG_NORMAL);

				this.callBackupService(src);
				
				// 处理原始报文状态
				this.callStatusService(src);
			}

			this.callNextStep(src);
		} catch (RpcException e) {
			throw new NeedResendSimpleException("Parser:", e);
		} catch (Exception e) {
			throw new ParserException("Parse Error:" + e.getMessage(), e);
		}

		logger.debug("Finish Parser......" + src.toLog());
	}

	private void procResendMax(SimpleMessage src) throws SimpleException {
		if (src.getResendCount() >= (maxCount - 1)) {
			setErrorInfos(SimpleConstants.ERROR_TYPE_RESEND, SimpleConstants.ERROR_LEVEL_CLASS,
					SimpleConstants.RESPONSE_CODE_RESEND, SimpleConstants.RESPONSE_MSG_RESEND);

			throw new CommonSimpleException("Resend Count is larger than MAX.");
		}
	}

	private void parse(SimpleMessage src) throws SimpleException {
		String msgFormat = src.getMsgFormat();

		if ("XML".equalsIgnoreCase(getMainFormat(msgFormat))) {
			XmlObject catdoc = null;
			try {
				// StringReader xmlfile = new StringReader(src.getContent());
				String xmlfile = src.getContent();
				
				// 去除多余字符
				if (!xmlfile.startsWith("<")) {
					int beginIndex = xmlfile.indexOf("<");
					xmlfile = xmlfile.substring(beginIndex);
					// System.out.println(beginIndex);
				}

				catdoc = XmlObject.Factory.parse(xmlfile);

				if (catdoc instanceof EC101MessageDocument) {
					logger.debug("Message busi Type:EC101MessageDocument");
					// 直接设置msgType和MsgBusiType
					com.simple.datax.xml.ec.MessageHeadDocument.MessageHead mh = ((EC101MessageDocument) catdoc)
							.getEC101Message().getMessageHead();
					procSimpleMessage(src, mh.getMessageType(), mh.getDataCompress(), mh.getDataResponse(),
							mh.getSenderAddress(), mh.getReceiverAddress(), mh.getMessageId(), mh.getMessageTime());

					src.setResponse(false);
					src.setMsgType("EC");
				} else if (catdoc instanceof EC101ResponseDocument) {
					logger.debug("Message busi Type:EC101ResponseDocument");
					com.simple.datax.xml.ec.MessageHeadDocument.MessageHead mh = ((EC101ResponseDocument) catdoc)
							.getEC101Response().getMessageHead();
					// 直接设置msgType和MsgBusiType
					procSimpleMessage(src, mh.getMessageType(), mh.getDataCompress(), mh.getDataResponse(),
							mh.getSenderAddress(), mh.getReceiverAddress(), mh.getMessageId(), mh.getMessageTime());

					src.setResponse(true);
					src.setMsgType("EC");
				} else if (catdoc instanceof TCS101MessageDocument) {
					logger.debug("Message busi Type:TCS101MessageDocument");
					com.simple.datax.xml.tcs.MessageHeadDocument.MessageHead mh = ((TCS101MessageDocument) catdoc)
							.getTCS101Message().getMessageHead();
					// 直接设置msgType和MsgBusiType 对于TCS过来的报文，都不做回执处理
					procSimpleMessage(src, mh.getMessageType(), mh.getDataCompress(), false, mh.getSenderAddress(),
							mh.getReceiverAddress(), mh.getMessageId(), mh.getMessageTime());

					src.setResponse(false);
					src.setMsgType("TCS");
				} else if (catdoc instanceof TCS101ResponseDocument) {
					logger.debug("Message busi Type:TCS101ResponseDocument");
					com.simple.datax.xml.tcs.MessageHeadDocument.MessageHead mh = ((TCS101ResponseDocument) catdoc)
							.getTCS101Response().getMessageHead();
					// 直接设置msgType和MsgBusiType 对于TCS过来的报文，都不做回执处理
					procSimpleMessage(src, mh.getMessageType(), mh.getDataCompress(), false, mh.getSenderAddress(),
							mh.getReceiverAddress(), mh.getMessageId(), mh.getMessageTime());

					src.setResponse(true);
					src.setMsgType("TCS");
				} else {
					// logger.error("Unkown Message business type!!");
					throw new ParserException("Unkown Message business type!!");
				}

				// 对于XML需要进行XSD校验。
				xsdValidate(catdoc);

				// 设置内部文件对象，并清除content的内容。
				src.setContent(null);
				src.setIntraDoc(catdoc);
				//logger.debug(catdoc.toString());
			} catch (XmlException e) {
				// e.printStackTrace();
				// logger.error("Parse Error:", e);
				throw new ParserException(e.getMessage(), e);
			} catch (Exception e) {
				// e.printStackTrace();
				// logger.error("Parse Error:", e);
				throw new ParserException( e.getMessage(), e);
			}
		} else {
			throw new ParserException("Not Supported message format:" + msgFormat);
		}
	}

	private void xsdValidate(XmlObject catdoc) throws SimpleException {
		ArrayList<XmlValidationError> validationErrors = new ArrayList<XmlValidationError>();
		XmlOptions m_validationOptions = new XmlOptions();
		m_validationOptions.setErrorListener(validationErrors);

		boolean ff = catdoc.validate(m_validationOptions);
		logger.debug("Validater :" + ff);
		if (!ff) {
			String errMsg = printErrors(validationErrors);
			throw new ParserException("XSD Validate　Error:" + errMsg);
		}
	}

	private String printErrors(ArrayList<XmlValidationError> validationErrors) {
		String errMsg = SimpleConstants.RESPONSE_MSG_VALIDATER + "\n";
		// logger.error("Errors discovered during validation: \n");
		Iterator<XmlValidationError> iter = validationErrors.iterator();
		while (iter.hasNext()) {
			XmlValidationError xe = iter.next();
			logger.error(">> " + xe + "\n");
			errMsg = errMsg + xe.getMessage() + "\n";
			// logger.error("  Column>> " + xe.getColumn() + "\n" );
			// logger.error("  ErrorCode>> " + xe.getErrorCode() + "\n" );
			// logger.error("  ErrorType>> " + xe.getErrorType() + "\n" );
			// logger.error("  Line>> " + xe.getLine() + "\n" );
			// logger.error("  Message>> " + xe.getMessage() + "\n" );
			// logger.error("  Offset>> " + xe.getOffset() + "\n" );
			// logger.error("  SourceName>> " + xe.getSourceName() + "\n" );
			// logger.error("  BadSchemaType>> " + xe.getBadSchemaType() + "\n"
			// );
		}
		return errMsg;
	}

	private void procSimpleMessage(SimpleMessage src, String busiType, String compress, boolean needResponse,
			String senderAddress, String receiverAddress, String msgId, String msgTime) {
		src.setMsgBusiType(busiType);

		boolean isCompress = false;
		try {
			isCompress = Boolean.parseBoolean(compress);
		} catch (Exception e) {
		}
		src.setCompress(isCompress);

		src.setNeedResponse(needResponse);

		// TODO:ServiceCode需要处理
		if (senderAddress.indexOf("@") > 0) {
			src.setSender(senderAddress.substring(0, senderAddress.indexOf("@")));
			src.setSenderNode(senderAddress.substring(senderAddress.indexOf("@") + 1));
		} else {
			src.setSender(senderAddress);
		}

		if (receiverAddress.indexOf("@") > 0) {
			src.setReceiver(receiverAddress.substring(0, receiverAddress.indexOf("@")));
			src.setReceiverNode(receiverAddress.substring(receiverAddress.indexOf("@") + 1));
		} else {
			src.setReceiver(receiverAddress);
		}

		src.setMessageid(msgId);
		// 此处先设置成一样，后续报文需要修改时在修改，但是大部分情况是和接收的msgID一样，只有在新生成报文的时候，是不一样的。
		src.setSentMessageid(msgId);
		src.setMsgTime(msgTime);
	}

	public int getMaxCount() {
		return maxCount;
	}

	public void setMaxCount(int maxCount) {
		this.maxCount = maxCount;
	}
}
